--  ---------------------------------------------------------------------------
--  @File: settings.adb
--
--  @Project: LiberAda
--  @Version: 1.0
--  @Created: 2009-09-23
--  @Author:  Markus Knauß
--
--  @Description:
--    For a detailed description of this package (@see settings.ads).
--  @Implementation:
--    Setter and getter functions for the LiberAda application's state.
--    Possibility to load the settings from given command line arguments.
--
--  @Modifications:
--    +2009-09-23 MK: Initial release
with Ada.Containers.Vectors;
with Ada.Directories;
with Ada.Text_IO;

with Command_Line;
with Errors;
with File_Tools;

package body Settings is

   package ASU renames Ada.Strings.Unbounded;

   package ASU_Vectors is new Ada.Containers.Vectors
     (Index_Type   => Natural,
      Element_Type => ASU.Unbounded_String,
      "="          => ASU."=");

   --  ------------------------------------------------------------------------
   --  @Variable: Is_Recursive
   --  @Purpose:
   --    Stores if recursive mode is enabled or not.
   Is_Recursive : Boolean := False;

   --  ------------------------------------------------------------------------
   --  @Variable: Ext
   --  @Purpose:
   --    Stores all file name extensions for which LiberAda will generate
   --    documentation.
   Ext : ASU_Vectors.Vector := ASU_Vectors.Empty_Vector;

   --  ------------------------------------------------------------------------
   --  @Variable: Output
   --  @Purpose:
   --    Stores the directory to which generated documentation is written.
   Output : ASU.Unbounded_String;

   --  ------------------------------------------------------------------------
   --  @Variable: Files
   --  @Purpose:
   --    Holds the names of all files for which documentation is generated.
   Files : ASU_Vectors.Vector := ASU_Vectors.Empty_Vector;

   --  ------------------------------------------------------------------------
   --  @Variable: Verb
   --  @Purpose:
   --    Stores if verbose mode is enabled or not.
   Verb : Boolean := False;

   --  ------------------------------------------------------------------------
   --  @Procedure: Set_Recursive
   procedure Set_Recursive (Recurse : Boolean) is
   begin
      Is_Recursive := Recurse;
   end Set_Recursive;

   --  ------------------------------------------------------------------------
   --  @Function: Recursive
   function Recursive return Boolean is
   begin
      return Is_Recursive;
   end Recursive;

   --  ------------------------------------------------------------------------
   --  @Procedure: Add_Extension
   procedure Add_Extension (Extension : in String) is
   begin
      Ext.Append (ASU.To_Unbounded_String (Extension));
   end Add_Extension;

   --  ------------------------------------------------------------------------
   --  @Function: Has_Allowed_Extension
   function Has_Allowed_Extension (File_Name : in String) return Boolean is
      File_Ext  : constant String := Ada.Directories.Extension (File_Name);
      Extension : ASU.Unbounded_String;
      Ext_Crsr  : ASU_Vectors.Cursor := Ext.First;
   begin
      if File_Ext'Length > 0 then
         while ASU_Vectors.Has_Element (Ext_Crsr) loop
            Extension := ASU_Vectors.Element (Ext_Crsr);
            if ASU.Length (Extension) >= File_Ext'Length and then
              File_Ext = ASU.To_String (ASU.Tail (
                Extension, File_Ext'Length))
            then
               return True;
            end if;
            Ext_Crsr := ASU_Vectors.Next (Ext_Crsr);
         end loop;
      end if;
      return False;
   end Has_Allowed_Extension;

   --  ------------------------------------------------------------------------
   --  @Function: Extensions
   function Extensions return Extensions_Type is
      Extensions_Array : Extensions_Type (
        Ext.First_Index .. Ext.Last_Index);
   begin
      for Extensions_Idx in Extensions_Array'Range loop
         Extensions_Array (Extensions_Idx) :=
           Ext.Element (Extensions_Idx);
      end loop;
      return Extensions_Array;
   end Extensions;

   --  ------------------------------------------------------------------------
   --  @Procedure: Set_Out_Directory
   procedure Set_Output_Directory (Output_Dir : in String) is
   begin
      Output := ASU.To_Unbounded_String (Output_Dir);
   end Set_Output_Directory;

   --  ------------------------------------------------------------------------
   --  @Function: Output_Directory
   function Output_Directory return String is
   begin
      return ASU.To_String (Output);
   end Output_Directory;

   --  ------------------------------------------------------------------------
   --  @Procedure: Add_Source_File
   procedure Add_Source_File (Name : in String) is
   begin
      Files.Append (ASU.To_Unbounded_String (Name));
   end Add_Source_File;

   --  ------------------------------------------------------------------------
   --  @Function: Has_Source_File
   function Has_Source_File (Name : in String) return Boolean is
   begin
      return Files.Find_Index (
        ASU.To_Unbounded_String (Name)) /= ASU_Vectors.No_Index;
   end Has_Source_File;

   --  ----------------------------------------------------------------------
   --  @Function: Has_Source_Files
   --  @Description: Determines if there are any source files.
   --  @Return: True if there are source files otherwise false.
   function Has_Source_Files return Boolean is
   begin
      return ASU_Vectors.Has_Element (Files.First);
   end Has_Source_Files;

   --  ------------------------------------------------------------------------
   --  @Function: Source_Files
   function Source_Files return Source_Files_Type is
      Files_Array : Source_Files_Type (Files.First_Index .. Files.Last_Index);
   begin
      for Files_Idx in Files_Array'Range loop
         Files_Array (Files_Idx) := Files.Element (Files_Idx);
      end loop;
      return Files_Array;
   end Source_Files;

   --  ------------------------------------------------------------------------
   --  @Procedure: Set_Verbose
   procedure Set_Verbose (Verbose : in Boolean) is
   begin
      Verb := Verbose;
   end Set_Verbose;

   --  ------------------------------------------------------------------------
   --  @Function: Verbose
   function Verbose return Boolean is
   begin
      return Verb;
   end Verbose;

   --  ------------------------------------------------------------------------
   --  @Procedure: Read_From_Command_Line
   procedure Read_From_Command_Line is
      --  Switches for the command line
      INCLUDE_PARAMETER_KEY    : constant String := "-i";
      RECURSE_PARAMETER_KEY    : constant String := "-r";
      OUTPUT_PARAMETER_KEY     : constant String := "-o";
      EXTENSIONS_PARAMETER_KEY : constant String := "-ext";
      VERBOSE_KEY              : constant String := "-v";
   begin
      --  Evaluate "-r" parameter
      Settings.Set_Recursive (Command_Line.Has_Key (RECURSE_PARAMETER_KEY));

      --  Evaluate "-ext" parameter
      if Command_Line.Has_Key (EXTENSIONS_PARAMETER_KEY) then
         if Command_Line.Has_Values (EXTENSIONS_PARAMETER_KEY) then
            declare
               Extensions : constant Command_Line.Values_Type :=
                 Command_Line.Get_Values (EXTENSIONS_PARAMETER_KEY);
            begin
               for Extension_Idx in Extensions'Range loop
                  Settings.Add_Extension (Ada.Strings.Unbounded.To_String (
                    Extensions (Extension_Idx)));
               end loop;
            end;
         else
            Errors.Print_Error (
              Errors.VALUES_FOR_EXTENSIONS_PARAMETER_MISSING);
         end if;
      else
         --  No extensions were specified. Therefore, add the default
         --  extensions to the list.
         Settings.Add_Extension (Settings.DEFAULT_BODY_EXTENSION);
         Settings.Add_Extension (Settings.DEFAULT_SPEC_EXTENSION);
      end if;

      --  The -i option is mandatory. If it is omitted or if no values are
      --  given, an error message is printed.
      if Command_Line.Has_Key (INCLUDE_PARAMETER_KEY) then
         if Command_Line.Has_Values (INCLUDE_PARAMETER_KEY) then
            declare
               Values : constant Command_Line.Values_Type :=
                 Command_Line.Get_Values (INCLUDE_PARAMETER_KEY);
               Name   : Ada.Strings.Unbounded.Unbounded_String;
            begin
               for Value_Itr in Values'Range loop
                  Name := ASU.To_Unbounded_String (
                    Ada.Directories.Full_Name (ASU.To_String (
                      Values (Value_Itr))));
                  if File_Tools.Is_Directory (ASU.To_String (Name)) then
                     declare
                        Files : constant File_Tools.Files_Type :=
                          File_Tools.Files_In_Directory (
                          ASU.To_String (Name), Settings.Recursive);
                     begin
                        for Files_Idx in Files'Range loop
                           if Settings.Has_Allowed_Extension (ASU.To_String (
                             Files (Files_Idx)))
                           then
                              if not Settings.Has_Source_File (ASU.To_String (
                                Files (Files_Idx)))
                              then
                                 Settings.Add_Source_File (ASU.To_String (
                                   Files (Files_Idx)));
                              end if;
                           end if;
                        end loop;
                     end;
                  else
                     if not Settings.Has_Source_File (
                        ASU.To_String (Name))
                     then
                        Settings.Add_Source_File (ASU.To_String (Name));
                     end if;
                  end if;
               end loop;
            end;
         else
            Errors.Print_Error (Errors.VALUES_FOR_INCLUDE_PARAMETER_MISSING);
         end if;
      else
         Errors.Print_Error (Errors.INCLUDE_PARAMETER_MISSING);
      end if;

      --  Evaluate "-o" parameter
      if Command_Line.Has_Key (OUTPUT_PARAMETER_KEY) then
         if Command_Line.Has_Values (OUTPUT_PARAMETER_KEY) then
            declare
               Outputs : constant Command_Line.Values_Type :=
                 Command_Line.Get_Values (OUTPUT_PARAMETER_KEY);
            begin
               if (Outputs'Length = 1) then
                  if File_Tools.Is_Directory (Ada.Strings.Unbounded.To_String (
                    Outputs (1)))
                  then
                     Settings.Set_Output_Directory (Ada.Strings.Unbounded.
                       To_String (Outputs (Outputs'First)));
                  else
                     Errors.Print_Error (
                       Errors.OUTPUT_LOCATION_IS_NO_DIRECTORY);
                  end if;
               else
                  Errors.Print_Error (Errors.MULTIPLE_OUTPUT_LOCATIONS);
               end if;
            end;
         else
            Errors.Print_Error (Errors.OUTPUT_LOCATION_VALUE_MISSING);
         end if;
      else
         --  No output directory was specified. Therefore, set the default
         --  output directory.
         Settings.Set_Output_Directory (Settings.DEFAULT_OUTPUT_DIRECTORY);
      end if;

      --  Evaluate "-v" parameter
      if Command_Line.Has_Key (VERBOSE_KEY) then
         Settings.Set_Verbose (True);
      end if;

      --  If verbose mode is set, print the settings of the application as
      --  specified on the command line.
      if Settings.Verbose then
         Ada.Text_IO.Put ("--- SETTINGS: -----------------------------------");
         Ada.Text_IO.Put_Line ("------------------------------------");
         Ada.Text_IO.Put_Line ("Recursive:        " &
           Boolean'Image (Settings.Recursive));
         Ada.Text_IO.Put_Line ("Output directory: " &
           Settings.Output_Directory);
         declare
            Extensions : constant Settings.Extensions_Type :=
              Settings.Extensions;
         begin
            Ada.Text_IO.Put_Line ("Extensions:");
            for Extensions_Idx in Extensions'Range loop
               Ada.Text_IO.Put_Line ("  Extension " & Integer'Image (
                 Extensions_Idx) & ": " &
                 Ada.Strings.Unbounded.To_String (
                   Extensions (Extensions_Idx)));
            end loop;
         end;
         declare
            Files : constant Settings.Source_Files_Type :=
              Settings.Source_Files;
         begin
            Ada.Text_IO.Put_Line ("Source files:");
            for Files_Idx in Files'Range loop
               Ada.Text_IO.Put_Line ("  File: " &
                 Ada.Strings.Unbounded.To_String (Files (Files_Idx)));
            end loop;
         end;
      end if;
   end Read_From_Command_Line;

end Settings;
